/*
    Copyright © 2014-2019 by The qTox Project Contributors

    This file is part of qTox, a Qt-based graphical interface for Tox.

    qTox is libre software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    qTox is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with qTox.  If not, see <http://www.gnu.org/licenses/>.
*/


#pragma once

#include "audio/iaudiocontrol.h"
#include "alsink.h"
#include "alsource.h"

#include <memory>
#include <unordered_set>
#include<fstream>
#include <atomic>
#include <cmath>

#include <mutex>
#include "timer.hpp"

#include <cassert>

#include "signal.hpp"


class IAudioSettings;

class OpenAL : public IAudioControl
{

public:
    OpenAL(IAudioSettings& _settings);
    virtual ~OpenAL();

    double maxOutputVolume() const
    {
        return 1;
    }
    double minOutputVolume() const
    {
        return 0;
    }
    double outputVolume() ;
    void setOutputVolume(double volume);

    double minInputGain() ;
    double maxInputGain() ;

    double inputGain() const;
    void setInputGain(double dB);

    double minInputThreshold() ;
    double maxInputThreshold() ;

    double getInputThreshold() ;
    void setInputThreshold(double normalizedThreshold);

    void reinitInput(const std::string& inDevDesc);
    bool reinitOutput(const std::string& outDevDesc);

    bool isOutputReady() ;

    std::list<std::string> outDeviceNames();
    std::list<std::string> inDeviceNames();

    std::unique_ptr<IAudioSink> makeSink();
    void destroySink(AlSink& sink);

    std::unique_ptr<IAudioSource> makeSource();
    void destroySource(AlSource& source);

    void startLoop(uint32_t sourceId);
    void stopLoop(uint32_t sourceId);
    void playMono16Sound(AlSink& sink, const IAudioSink::Sound& sound);
    void stopActive();

    void playAudioBuffer(uint32_t sourceId, const int16_t* data, int samples, unsigned channels,
                         int sampleRate);
//signals:
    sigslot::signal <double> startActive;

protected:
    static void checkAlError() noexcept;

    double inputGainFactor() const;
    virtual void cleanupInput();
    virtual void cleanupOutput();

    bool autoInitInput();
    bool autoInitOutput();

    bool initInput(const std::string& deviceName, uint32_t channels);

    void doAudio();

    virtual void doInput();
    virtual void doOutput();

private:
    virtual bool initInput(const std::string& deviceName);
    virtual bool initOutput(const std::string& outDevDescr);

    void cleanupBuffers(uint32_t sourceId);
    void cleanupSound();

    float getVolume();
    inline bool is_file_exist(const char *fileName)
    {
        std::ifstream infile(fileName);
        return infile.good();
    }
protected:
    IAudioSettings& settings;
    std::thread* audioThread;
     std::mutex audioLock;
    std::string inDev{};
    std::string outDev{};


    bool outputInitialized = false;

    // Qt containers need copy operators, so use stdlib containers
    std::unordered_set<AlSink*> sinks;
    std::unordered_set<AlSink*> soundSinks;
    std::unordered_set<AlSource*> sources;

    int channels = 0;
    double gain = 0;
    double gainFactor = 1;
    static constexpr double minInGain = -30;
    static constexpr double maxInGain = 30;
    double inputThreshold = 0;
    double voiceHold = 250;
    bool isActive = false;
    //Timer voiceTimer;
    const double minInThreshold = 0.0;
    const double maxInThreshold = 0.4;
    int16_t* inputBuffer = nullptr;
};
